﻿/*
 * В рамкаж данного класса происходит Обучение НС двумя методами
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace DiplomNN
{
    class Training
    {
        const int L1 = 2500;
        const int L2 = 200;
        static int L3 = Helper.L3;
        const double Lambda = 0.15;
        const double Ee = 0.15;
        public static List<string> Types = new List<string> { "png", "PNG", "jpeg", "JPEG", "bmp", "BMP", "jpg", "JPG" };
        static double[,] Wij = new double[L1, L2];
        static double[,] Wjk = new double[L2, L3];
        static double[] Tj = new double[L2];
        static double[] Tk = new double[L3];        
        static double[] Layer2 = new double[L2];
        static double[] Layer3 = new double[L3];
        static double[] Error_k = new double[L3];
        static double[] Error_j = new double[L2];
        

        public static void Train ()
        {
            Helper.ArrPict = new TrainPicture[Helper.Directories.Length];
            for (int i = 0; i < Helper.Directories.Length; i++)
            {
                string[] files = Directory.GetFiles(Helper.Directories[i]);
                char separator = '\\';
                string[] divided = Helper.Directories[i].Split(separator);
                Helper.ArrPict[i] = new TrainPicture(divided[divided.Length - 1], files.Length);
                for (int j = 0; j < files.Length; j++)
                {
                    int flag = 0;
                    foreach (var fi in Types)
                    {
                        char sep = '.';
                        string[] div = files[j].Split(sep);                        
                        if (fi.Equals(div[div.Length - 1]))
                        {
                            flag = 1;
                            break;
                        }
                    }
                    if (flag == 1)
                    {
                        Bitmap Bm = new Bitmap(files[i]);
                        double[] Pic = BodyRecogn.ByteRGBToBynaryArr(Colors.BitmapToByteRgb(Bm));
                        for (int k = 0; k< Pic.Length; k++)
                        {
                            Helper.ArrPict[i].Arr[j, k] = Pic[k]; 
                        }
                    }
                }
 
            }

            /*
             * Начальная инициализация весовых коэффициентов и пороговых значений
             */

            Random rnd = new Random(DateTime.Now.Millisecond);
            for (int i = 0; i < L1; i++)
            {
                for (int j = 0; j < L2; j++)
                {                    
                    Wij[i, j] = ((double)(rnd.Next(40) - 20)) / 100.0;                    
                }
            }

            for (int j = 0; j < L2; j++)
            {  
                for (int k = 0; k < L3; k++)
                {
                    Wjk[j, k] = ((double)(rnd.Next(40) - 20)) / 100.0;
                }
                Tj[j] = 1;
                //Tj[j] = ((double)(rnd.Next(20) - 10)) / 100.0;
            }

            for (int k = 0; k < L3; k++)
            {
                Tk[k] = 1;
                //Tk[k] = ((double)(rnd.Next(20) - 10)) / 100.0;
            }
                        
            /*
             * Обучение
             */
            
            double Es = 10;
            int Sh = 0;
            while (Es > Ee)
            {
                Sh++;
                if (Es < 1)
                {
                    int a;
                    a = 10;
                }
                double S;
                // первый проход по обучающей выборке с целью модификации весовых коэффициентов
                for (int i = 0; i < Helper.ArrPict.Length; i++) 
                {
                    for (int j = 0; j < Helper.ArrPict[i].Arr.GetLength(0); j++)
                    {
                        # region Фаза прямого распростронения
                        S = 0;
                        for (int k = 0; k < L2; k++)//Для скрытого слоя
                        {
                            for (int l = 0; l < L1; l++)
                            {
                                S += Wij[l, k] * Helper.ArrPict[i].Arr[j, l];
                            }
                            S = S - Tj[k];                            
                            Layer2[k] = (double)(1 / (1 + Math.Pow(2.718281, (-S))));
                            S = 0;
                        }

                        S = 0;
                        for (int k = 0; k < L3; k++)//Для выходного слоя
                        {
                            for (int l = 0; l < L2; l++)
                            {
                                S += Wjk[l, k] * Layer2[l];
                            }
                            S = S - Tk[k];
                            Layer3[k] = (double)(1 / (1 + Math.Pow(2.718281, (-S))));
                            S = 0;
                        }
                        /*Double Max = Layer3.Max();
                        for (int k = 0; k < L3; k++)
                        {
                            if (Layer3[k] == Max)
                            {
                                Layer3[k] = 1;
                            }
                            else
                            {
                                Layer3[k] = 0;
                            }
                        }*/
                        # endregion
                        # region Фаза Обратного распространения
                        double Etalon_k;
                        for (int k = 0; k < L3; k++) //Ошибки выходого слоя
                        {
                            if(k == i)
                            {
                                Etalon_k = 1;
                            }
                            else
                            {
                                Etalon_k = 0;
                            }
                            Error_k[k] = Layer3[k] - Etalon_k;
                        }
                        for (int l = 0; l < L2; l++) // Ошибки скрытого слоя
                        {
                            for (int k = 0; k < L3; k++)
                            {
                                Error_j[l] += Error_k[k] * Wjk[l, k] * (Layer3[k] * (1 - Layer3[k]));
                            }
                        }
                        # region Модификация весовых коэффициентов и пороговых значений
                        for (int l = 0; l < L2; l++)// Модификация Wjk
                        {
                            for (int k = 0; k < L3; k++)
                            {
                                Wjk[l, k] = Wjk[l, k] - Lambda * Error_k[k] * Layer2[l] * (Layer3[k] * (1 - Layer3[k])); 
                            }
                        }

                        for (int k = 0; k < L3; k++)// Модификация Tk
                        {
                            Tk[k] = Tk[k] + Lambda * Error_k[k] * (Layer3[k] * (1 - Layer3[k]));
                        }

                        for (int l = 0; l < L1; l++)// Модификация Wij
                        {
                            for (int k = 0; k < L2; k++)
                            {
                                Wij[l, k] = Wij[l, k] - Lambda * Error_j[k] * Helper.ArrPict[i].Arr[j, l] * (Layer2[k] * (1 - Layer2[k]));
                            }
                        }

                        for (int k = 0; k < L2; k++)// Модификация Tj
                        {
                            Tj[k] = Tj[k] + Lambda * Error_j[k] * (Layer2[k] * (1 - Layer2[k]));
                        }
                        # endregion

                        # endregion
                    }
                }

                // Второй проход по обучающей выборке с целью вычисления суммарной квадратичной ошибки
                Es = 0;
                for (int i = 0; i < Helper.ArrPict.Length; i++)
                {
                    for (int j = 0; j < Helper.ArrPict[i].Arr.GetLength(0); j++)
                    {
                        S = 0;
                        for (int k = 0; k < L2; k++)//Для скрытого слоя
                        {
                            for (int l = 0; l < L1; l++)
                            {
                                S += Wij[l, k] * Helper.ArrPict[i].Arr[j, l];
                            }
                            S = S - Tj[k];
                            Layer2[k] = (double)((1 / (1 + Math.Pow(2.718281, (-S)))));
                            S = 0;
                        }

                        S = 0;
                        for (int k = 0; k < L3; k++)//Для выходного слоя
                        {
                            for (int l = 0; l < L2; l++)
                            {
                                S += Wjk[l, k] * Layer2[l];
                            }
                            S = S - Tk[k];
                            Layer3[k] = (double)((1 / (1 + Math.Pow(2.718281, (-S)))));
                            S = 0;
                        }
                       /* Double Max = Layer3.Max();
                        for (int k = 0; k < L3; k++)
                        {
                            if (Layer3[k] == Max)
                            {
                                Layer3[k] = 1;
                            }
                            else
                            {
                                Layer3[k] = 0;
                            }
                        }*/

                        double Etalon_k;
                        for (int k = 0; k < L3; k++)
                        {
                            if(k == i)
                            {
                                Etalon_k = 1;
                            }
                            else
                            {
                                Etalon_k = 0;
                            }
                            Es += Math.Pow((Layer3[k] - Etalon_k), 2);
                        }
                    }
                }
                Es = Es / 2;
               
            }

            Helper.Wij = Wij;
            Helper.Wjk = Wjk;
            Helper.Tj = Tj;
            Helper.Tk = Tk;
            Helper.IsTrainedNNMP = true;
            Helper.IsTrainedNNHopfild = false;
 
        }

        public static void TrainHopfild()
        {
            string[] files = Directory.GetFiles(Helper.HopfildDirPath);
            int ImageFileCount = files.Length;
            for (int i = 0; i < files.Length; i++)
            {
                if (TrainingForm.GetFileType(files[i]) == "NNHP")
                {
                    ImageFileCount--;
                }
            }
            if (ImageFileCount > 1)
            {
                Helper.ArrPict = new TrainPicture[ImageFileCount];
                int l = 0;
                for (int i = 0; i <files.Length; i++)
                {
                    if (TrainingForm.GetFileType(files[i]) != "NNHP")
                    {
                        Helper.ArrPict[l] = new TrainPicture(TrainingForm.GetFileName(files[i]), 1);
                        Bitmap Bm = new Bitmap(files[i]);
                        double[] Pic = BodyRecogn.ByteRGBToBynaryArr(Colors.BitmapToByteRgb(Bm));
                        for (int k = 0; k < Pic.Length; k++)
                        {
                            Helper.ArrPict[l].Arr[0, k] = Pic[k];
                            if (Pic[k] == 1)
                            {
                                Helper.ArrPict[l].ArrPolar[k] = 1;
                            }
                            else
                            {
                                Helper.ArrPict[l].ArrPolar[k] = -1;
                            }
                        }
                        l++;
                    }
                    else 
                    {
                        continue;
                    }
                }

                for (int i = 0; i < 2500; i++)
                {
                    for (int j = 0; j < 2500; j++)
                    {
                        Helper.GlobalArrayNN[i, j] = 0;
                    }
                }
                
                for (int k = 0; k < 2500; k++)
                {
                    for (int i = 0; i < 2500; i++)
                    {
                        for (int kk = 0; kk < Helper.ArrPict.Length; kk++)
                        {
                            Helper.GlobalArrayNN[k, i] += Helper.ArrPict[kk].ArrPolar[k] * Helper.ArrPict[kk].ArrPolar[i];
                        }                                             
                    }
                }
                for (int i = 0; i < 2500; i++)
                {
                    for (int j = 0; j < 2500; j++)
                    {
                        if (i == j)
                        {
                            Helper.GlobalArrayNN[i, j] = 0;
                        }                        
                    }
                }
                Helper.IsTrainedNNMP = false;
                Helper.IsTrainedNNHopfild = true;
            }
            else
            {
                 MessageBox.Show("Изображений для обучения не найдено!", "Внимание!",MessageBoxButtons.OK);
            }
        }
    }
}
